﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Noris.Schedule.Support.Green;

namespace Noris.Tools.FrxEditor.Data
{
    #region class SimpleDataGrid : Grid, který zobrazuje data z paměťové tabulky SimpleDataTable
    /// <summary>
    /// SimpleDataGrid : Grid, který zobrazuje data z paměťové tabulky SimpleDataTable
    /// </summary>
    public class SimpleDataGrid : DataGridView
    {
        #region Konstrukce, instanční proměnné
        public SimpleDataGrid()
        {
            this.InitializeProperty();
        }
        private void InitializeProperty()
        {
            this.AllowDrop = false;
            this.AllowUserToOrderColumns = true;
            this.AllowUserToResizeColumns = true;
            this.AllowUserToResizeRows = false;
            this.AutoGenerateColumns = false;
            this.AutoSize = false;
            this.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
            this.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None;
            this.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText;
            this.ColumnHeadersHeight = 45;
            this.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing;
            this.ColumnHeadersVisible = true;
            this.EnableHeadersVisualStyles = true;
            this.MultiSelect = true;
            this.RowHeadersWidth = 23;
            this.ScrollBars = System.Windows.Forms.ScrollBars.Both;
            this.ShowEditingIcon = false;
            this.StandardTab = false;

            this.DoubleBuffered = true;

            this.DefaultCellStyle = new DataGridViewCellStyle();
            this.DefaultCellStyle.SelectionBackColor = Color.BlueViolet;
            this.DefaultCellStyle.SelectionForeColor = Color.Wheat;
            this.DefaultCellStyle.NullValue = "";
            this.DefaultCellStyle.ForeColor = Color.Black;
            this.DefaultCellStyle.BackColor = Color.Wheat;

            this.DataError += new DataGridViewDataErrorEventHandler(_Grid_DataError);
        }
        void _Grid_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            // ignorovat
        }
        private SimpleDataGridEditMode _SimpleEditMode;
        private SimpleDataTable _DataTable;
        #endregion
        #region Public property, eventy a metody
        /// <summary>
        /// Režim editace
        /// </summary>
        public SimpleDataGridEditMode SimpleEditMode
        {
            get { return this._SimpleEditMode; }
            set
            {
                this._SimpleEditMode = value;
                this._AcceptEditMode();
            }
        }
        /// <summary>
        /// Tabulka, jejíž data jsou zobrazena.
        /// Po vložení nového objektu dojde k reloadu jeho dat do tabulky.
        /// Property je autoinicializační: pokud bude uživatel číst hodnotu, a dosud nebude tabulka vytvořena, vytvoří se nová.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public SimpleDataTable DataTable
        {
            get
            {
                if (this._DataTable == null)
                    this._DataTable = SimpleDataTable.Empty;
                return this._DataTable;
            }
            set
            {
                this._DataTable = value;
                if (value != null)
                    value.DataGrid = this;
                this._ReloadContent();
            }
        }
        /// <summary>
        /// Obsahuje aktuálně vybrané řádky, typované na zdejší typ řádků (SimpleDataGridRow).
        /// Pozor: řádky musejí být vybrané jako celek (za svoje záhlaví) = nestačí jedna buňka.
        /// Jinou možnost nabízí property SelectedSimpleGridRowsFromCell.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IEnumerable<SimpleDataGridRow> SelectedSimpleGridWholeRows
        {
            get
            {
                List<SimpleDataGridRow> result = new List<SimpleDataGridRow>();

                foreach (DataGridViewRow row in this.SelectedRows)
                {
                    SimpleDataGridRow dgRow = row as SimpleDataGridRow;
                    if (dgRow != null)
                        result.Add(dgRow);
                }
                return result;
            }
        }
        /// <summary>
        /// Obsahuje aktuálně vybrané řádky, typované na zdejší typ řádků (SimpleDataGridRow).
        /// Pozor: řádek do tohoto výběru se dostane tak, že je vybraná jakákoli jeho buňka (Cell), nemusí být vybrán řádek jako celek (za svoje záhlaví).
        /// Jinou možnost nabízí property SelectedSimpleGridWholeRows.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IEnumerable<SimpleDataGridRow> SelectedSimpleGridRowsFromCell
        {
            get
            {
                Dictionary<int, SimpleDataGridRow> result = new Dictionary<int, SimpleDataGridRow>();

                foreach (DataGridViewCell cell in this.SelectedCells)
                {
                    if (!result.ContainsKey(cell.RowIndex))
                    {
                        SimpleDataGridRow dgRow = cell.OwningRow as SimpleDataGridRow;
                        if (dgRow != null)
                            result.Add(cell.RowIndex, dgRow);
                    }
                }
                return result.Values.ToList();
            }
        }
        /// <summary>
        /// Obsahuje aktuálně vybrané řádky, typované na datový typ řádků (SimpleDataRow).
        /// Pozor: řádek do tohoto výběru se dostane tak, že je vybraná jakákoli jeho buňka (Cell), nemusí být vybrán řádek jako celek (za svoje záhlaví).
        /// Jinou možnost nabízí property SelectedSimpleGridWholeRows.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IEnumerable<SimpleDataRow> SelectedSimpleDataRowsFromCell
        {
            get
            {
                Dictionary<int, SimpleDataRow> result = new Dictionary<int, SimpleDataRow>();

                foreach (DataGridViewCell cell in this.SelectedCells)
                {
                    if (!result.ContainsKey(cell.RowIndex))
                    {
                        SimpleDataGridRow dgRow = cell.OwningRow as SimpleDataGridRow;
                        if (dgRow != null)
                            result.Add(cell.RowIndex, dgRow.DataRow);
                    }
                }
                return result.Values.ToList();
            }
        }
        /// <summary>
        /// Událost volaná před změnou hodnoty. Změnu lze odvolat, lze modifikovat ukládanou hodnotu.
        /// </summary>
        public event SimpleDataValueChangingHandler ValueChanging;
        /// <summary>
        /// Událost volaná po změně hodnoty. Změnu již nelze odvolat.
        /// </summary>
        public event SimpleDataValueChangeHandler ValueChanged;
        /// <summary>
        /// Vyvolá událost ValueChanging.
        /// Proběhne těsně před vložením nové hodnoty do tabulky.
        /// Handler může hodnotu změnit nebo může změnu odmítnout.
        /// </summary>
        internal void OnValueChanging(object sender, SimpleDataValueChangingArgs args)
        {
            if (this.ValueChanging != null)
                this.ValueChanging(sender, args);
        }
        /// <summary>
        /// Vyvolá událost ValueChanged.
        /// Proběhne až poté, kdy je do tabulky vložena nová hodnota.
        /// </summary>
        internal void OnValueChange(SimpleDataRow sender, SimpleDataValueChangeArgs args)
        {
            if (this.ValueChanged != null)
                this.ValueChanged(sender, args);
        }
        /// <summary>
        /// Metoda smaže kompletně celou tabulku (sloupce + řádky).
        /// </summary>
        public void Clear()
        {
            if (this._DataTable != null)
            {
                this._DataTable.Rows.Clear();
                this._DataTable.Columns.Clear();
            }
        }
        /// <summary>
        /// Metoda smaže pouze řádky, nechá sloupce.
        /// </summary>
        public void ClearRows()
        {
            if (this._DataTable != null)
                this._DataTable.Rows.Clear();
        }
        #endregion
        #region Load data from table
        /// <summary>
        /// Smaže celý obsah tabulky, jen vizuální prvky (řádky + sloupce)
        /// </summary>
        private void _ClearData()
        {
            this.Rows.Clear();
            this.Columns.Clear();
        }
        private void _ReloadContent()
        {
            this._ClearData();

            SimpleDataTable table = this._DataTable;
            if (table == null) return;
            if (table.ColumnCount == 0) return;

            this.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;      // Výrazný vliv: 2000 => 510 milisec
            this.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None;            // Téměř bez vlivu: 2000 => 1850
            this.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing;  // Výrazný vliv: 510 => 35
            this.EnableHeadersVisualStyles = true;                                                      // Bez vlivu na rychlost
            this.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.EnableResizing;          // Minimální vliv na rychlost

            // Pokud se sloupce a řádky vkládají s nastavením různých property na Autosize, 
            //  pak se výrazně prodlužuje čas načítání (téměř na desetinásobek !!!)
            this._LoadColumns(table.Columns);        // 1000 milisec / 137 columns
            this._LoadRows(table.Rows);              // dalších 1000 milisec / 750 řádků × 137 columns, z toho 860 milisec = vložení řádku do Rows

            if (false)
            {
                this.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;           // Aplikace po vložení všech 450 řádků: 110 => 199 milisec
                this.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; // 199 => 205 milisec
            }
        }
        /// <summary>
        /// Načte sloupce z DataTable (dodané jako parametr) do gridu
        /// </summary>
        /// <param name="columns"></param>
        private void _LoadColumns(SimpleDataColumnCollection columns)
        {
            foreach (SimpleDataColumn column in columns)
            {
                SimpleDataGridColumn dgCol = new SimpleDataGridColumn(column);
                dgCol.Name = column.ColumnName;
                dgCol.HeaderText = column.HeaderText;
                dgCol.DefaultCellStyle = this.DefaultCellStyle.Clone();
                if (column.ColumnWidth.HasValue) dgCol.Width = column.ColumnWidth.Value;
                if (column.FillWeight.HasValue) dgCol.FillWeight = column.FillWeight.Value;
                if (column.AutoSizeMode != DataGridViewAutoSizeColumnMode.NotSet) dgCol.AutoSizeMode = column.AutoSizeMode;
                if (column.ToolTipText != null) dgCol.ToolTipText = column.ToolTipText;
                dgCol.Visible = column.GridVisible;
                this.Columns.Add(dgCol);
            }
        }
        /// <summary>
        /// Načte řádky z DataTable (dodané jako parametr) do gridu
        /// </summary>
        /// <param name="rows"></param>
        private void _LoadRows(SimpleDataRowCollection rows)
        {
            foreach (SimpleDataRow row in rows)
                this._AddRow(row);
        }
        /// <summary>
        /// Metoda smaže všechny vizuální sloupce.
        /// Volá se interně, z datové tabulky. Pokud by se volala přímo, pak by se smazaly jen vizuální sloupce, ale datové by zůstaly.
        /// </summary>
        public void ClearColumnsInternal()
        {
            this.Columns.Clear();
        }
        /// <summary>
        /// Přidá do gridu řádek.
        /// Řádek nepřidává do dat, tam odsud právě přichází tento požadavek na přidání datového řádku do vizuální vrstvy.
        /// </summary>
        /// <param name="row"></param>
        public void AddDataRowInternal(SimpleDataRow row)
        {
            int currentColumn = this.CurrentCellAddress.X;
            if (currentColumn < 0)
            {
                DataGridViewColumn column = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible);
                if (column != null)
                    currentColumn = column.Index;
            }
            SimpleDataGridRow dgRow = this._AddRow(row);
            int rowIndex = this.Rows.Count - 1;
            this.SetCurrentCellAddressCore(currentColumn, rowIndex, false, false, false);
            this.FirstDisplayedScrollingRowIndex = rowIndex;
        }
        /// <summary>
        /// Přidá do gridu řádek, neřeší nic víc.
        /// </summary>
        /// <param name="row"></param>
        private SimpleDataGridRow _AddRow(SimpleDataRow row)
        {
            SimpleDataGridRow dgRow = new SimpleDataGridRow(row);
            object[] values = row.GetValues();
            for (int c = 0; c < values.Length; c++)
            {
                SimpleDataGridColumn column = this.Columns[c] as SimpleDataGridColumn;
                SimpleDataRenderAsType renderAs = column.DataColumn.RenderAs;
                DataGridViewCell cell = null;
                switch (renderAs)
                {
                    case SimpleDataRenderAsType.ComboBox:
                        cell = new SimpleDataGridComboCell();
                        break;
                    default:
                        cell = new SimpleDataGridTextCell();
                        break;
                }
                if (cell != null)
                {
                    dgRow.Cells.Add(cell);
                }
            }
            this.Rows.Add(dgRow);
            return dgRow;
        }
        /// <summary>
        /// Odebere z gridu všechny GridRow, které v sobě obsahují referenci na daný datový řádek.
        /// Řádek neodebírá z dat, tam odsud právě přichází tento požadavek na odebrání datového řádku z vizuální vrstvy.
        /// </summary>
        /// <param name="row"></param>
        public void RemoveDataRowInternal(SimpleDataRow row)
        {
            int startIndex = 0;
            while (true)
            {
                int index = -1;
                for (int i = startIndex; i < this.Rows.Count; i++)
                {
                    SimpleDataGridRow dgRow = this.Rows[i] as SimpleDataGridRow;
                    if (dgRow != null && Object.ReferenceEquals(dgRow.DataRow, row))
                    {
                        index = i;
                        break;
                    }
                }
                if (index < 0) break;

                this.Rows.RemoveAt(index);
                startIndex = index;
            }
        }
        /// <summary>
        /// Smaže všechny řádky.
        /// Řádky nemaže z dat, tam odsud právě přichází tento požadavek na smazání řádků do vizuální vrstvy.
        /// </summary>
        public void ClearRowsInternal()
        {
            this.Rows.Clear();
        }
        #endregion
        #region Set edit mode
        private void _AcceptEditMode()
        {
            SimpleDataGridEditMode editMode = this._SimpleEditMode;
            bool editContent = (editMode == SimpleDataGridEditMode.EditContent || editMode == SimpleDataGridEditMode.EditContentAndRows);
            bool editRows = (editMode == SimpleDataGridEditMode.EditContentAndRows);

            this.AllowUserToAddRows = editRows;
            this.AllowUserToDeleteRows = editRows;
            this.ReadOnly = !editContent;
        }
        #endregion
    }
    #endregion
    #region class SimpleDataGridColumn : vizuální sloupec s daty
    /// <summary>
    /// SimpleDataGridColumn : vizuální sloupec s daty
    /// </summary>
    public class SimpleDataGridColumn : DataGridViewColumn
    {
        /// <summary>
        /// Bezparametrický konstruktor musí existovat, ale nesmí se používat.
        /// </summary>
        public SimpleDataGridColumn()
        {
            throw new FrxException("Create a instance of class SimpleDataGridColumn without parameter (column) is not allowed.");
        }
        /// <summary>
        /// Konstruktor s převzetím reference na datový sloupec
        /// </summary>
        /// <param name="column"></param>
        public SimpleDataGridColumn(SimpleDataColumn column)
        {
            this._DataColumn = column;
        }
        /// <summary>
        /// Data načteného sloupce
        /// </summary>
        public SimpleDataColumn DataColumn { get { return this._DataColumn; } }
        private SimpleDataColumn _DataColumn;
    }
    #endregion
    #region class SimpleDataGridRow : vizuální řádek s daty
    /// <summary>
    /// SimpleDataGridRow : vizuální řádek s daty
    /// </summary>
    public class SimpleDataGridRow : DataGridViewRow
    {
        /// <summary>
        /// Bezparametrický konstruktor musí existovat, ale nesmí se používat.
        /// </summary>
        public SimpleDataGridRow()
        {
            throw new FrxException("Create a instance of class SimpleDataGridRow without parameter (row) is not allowed.");
        }
        /// <summary>
        /// Konstruktor s převzetím reference na datový řádek
        /// </summary>
        /// <param name="row"></param>
        public SimpleDataGridRow(SimpleDataRow row)
        {
            this._DataRow = row;
        }
        /// <summary>
        /// Vrací klon. Při editaci a jiných operacích se pracuje s vyklonovaným objektem, který vzniká touto metodou.
        /// Bázová metoda využívá bezparametrický konstruktor, což není v této třídě vhodná technika.
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return this.MemberwiseClone() as SimpleDataGridRow;
        }
        /// <summary>
        /// Data řádku
        /// </summary>
        public SimpleDataRow DataRow { get { return this._DataRow; } }
        private SimpleDataRow _DataRow;
    }
    #endregion
    #region class SimpleDataGridTextCell : TextBox
    /// <summary>
    /// SimpleDataGridTextCell : TextBox
    /// </summary>
    public class SimpleDataGridTextCell : DataGridViewTextBoxCell
    {
        /// <summary>
        /// Je možno editovat?
        /// </summary>
        public bool Enabled
        {
            get { return this._Enabled; }
            set
            {
                this._Enabled = value;
                if (this.Style == null)
                    this.Style = this.InheritedStyle.Clone();
                this.Style.BackColor = (value ? this.InheritedStyle.BackColor : Color.Gray);
            }
        }
        private bool _Enabled;
        protected override void OnEnter(int rowIndex, bool throughMouseClick)
        {
            base.OnEnter(rowIndex, throughMouseClick);
            this.DataGridView.InvalidateRow(rowIndex);
        }
        protected override void OnLeave(int rowIndex, bool throughMouseClick)
        {
            base.OnLeave(rowIndex, throughMouseClick);
            this.DataGridView.InvalidateRow(rowIndex);
        }
        protected override object GetValue(int rowIndex)
        {
            return SimpleDataGridSupport.ReadValue(this);
        }
        protected override object GetFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
        {
            object fv = base.GetFormattedValue(value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context);
            return fv;
        }
        public override object ParseFormattedValue(object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter)
        {
            return base.ParseFormattedValue(formattedValue, cellStyle, formattedValueTypeConverter, valueTypeConverter);
        }
        protected override bool SetValue(int rowIndex, object value)
        {
            return SimpleDataGridSupport.StoreValue(this, value);
        }
        protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
        {
            SimpleCellJoinMode joinMode = SimpleDataGridSupport.DetectCellJoinY(this, rowIndex, ref formattedValue, ref advancedBorderStyle);

            SimpleDataGridSupport.PaintBackground(graphics, this, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, ref paintParts, joinMode);

            base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);

            SimpleDataGridColumn column = this.OwningColumn as SimpleDataGridColumn;
            if (column != null && column.DataColumn.HasRelation)
                SimpleDataGridSupport.PaintRelation(graphics, this, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, ref paintParts);
        }
    }
    #endregion
    #region class SimpleDataGridComboCell : ComboBox
    /// <summary>
    /// SimpleDataGridComboCell : ComboBox
    /// </summary>
    public class SimpleDataGridComboCell : DataGridViewComboBoxCell
    {
        public SimpleDataGridComboCell()
        {
            this.DisplayStyleForCurrentCellOnly = true;
            this.DisplayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton;
            this.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
        }
        protected override void OnEnter(int rowIndex, bool throughMouseClick)
        {
            this._CheckLoadItems();
            base.OnEnter(rowIndex, throughMouseClick);
            this.DataGridView.InvalidateRow(rowIndex);
        }
        protected override void OnLeave(int rowIndex, bool throughMouseClick)
        {
            base.OnLeave(rowIndex, throughMouseClick);
            this.DataGridView.InvalidateRow(rowIndex);
        }
        protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
        {
            SimpleCellJoinMode joinMode = SimpleDataGridSupport.DetectCellJoinY(this, rowIndex, ref formattedValue, ref advancedBorderStyle);
            SimpleDataGridSupport.PaintBackground(graphics, this, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, ref paintParts, joinMode);
            // advancedBorderStyle.Right = DataGridViewAdvancedCellBorderStyle.None;
            base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
        }
        protected override object GetValue(int rowIndex)
        {
            this._CheckLoadItems();
            object value = SimpleDataGridSupport.ReadValue(this);
            SimpleDataFormatter formatter = this.Formatter;
            if (formatter != null)
                value = formatter.GetDisplayValue(value);
            return value;
        }
        protected override bool SetValue(int rowIndex, object value)
        {
            SimpleDataFormatter formatter = this.Formatter;
            if (formatter != null)
                value = formatter.GetDataValue(value);

            return SimpleDataGridSupport.StoreValue(this, value);
        }
        private void _CheckLoadItems()
        {
            if (this._ItemsLoaded) return;

            this.Items.Clear();
            
            SimpleDataFormatter formatter = this.Formatter;
            if (formatter == null) return;

            IEnumerable<IEditStyleItem> items = formatter.EditStyleItems;
            if (items != null)
            {
                foreach (IEditStyleItem item in items)
                    this.Items.Add(item.DisplayText);
            }

            this._ItemsLoaded = true;
        }
        /// <summary>
        /// Formátovač dat pro tuto buňku = dotahuje se ze sloupce
        /// </summary>
        public SimpleDataFormatter Formatter
        {
            get
            {
                if (this._Formatter != null) return this._Formatter;
                SimpleDataGridColumn column = this.OwningColumn as SimpleDataGridColumn;
                if (column != null) return column.DataColumn.Formatter;
                return null;
            }
            set
            {
                this._Formatter = value;
                this._ItemsLoaded = false;
            }
        }
        private SimpleDataFormatter _Formatter;
        private bool _ItemsLoaded;
    }
    #endregion
    #region class SimpleDataGridSupport : metoda pro podporu akcí v třídách SimpleDataGrid???Cell (formátování hodnoty, vykreslení UI)
    /// <summary>
    /// SimpleDataGridSupport : metoda pro podporu akcí v třídách SimpleDataGrid???Cell (formátování hodnoty, vykreslení UI)
    /// </summary>
    public static class SimpleDataGridSupport
    {
        /// <summary>
        /// Zajistí vykreslení pozadí pod prvkem gridu podle jeho aktuálního stavu a 
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="cell"></param>
        /// <param name="clipBounds"></param>
        /// <param name="cellBounds"></param>
        /// <param name="rowIndex"></param>
        /// <param name="cellState"></param>
        /// <param name="value"></param>
        /// <param name="formattedValue"></param>
        /// <param name="errorText"></param>
        /// <param name="cellStyle"></param>
        /// <param name="advancedBorderStyle">Styl okrajů. Jde o class, nemusí se předávat jako ref.</param>
        /// <param name="paintParts">Jaké prvky se mají kreslit bázovou třídou. Jde o enum, předává se ref.</param>
        public static void PaintBackground(Graphics graphics, DataGridViewCell cell, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, ref DataGridViewPaintParts paintParts, SimpleCellJoinMode joinMode)
        {
            SimpleDataGridRow gridRow = cell.OwningRow as SimpleDataGridRow;
            SimpleDataGridColumn gridCol = cell.OwningColumn as SimpleDataGridColumn;
            if (gridRow == null || gridCol == null) return;
            SimpleDataRow dataRow = gridRow.DataRow;
            SimpleDataColumn dataCol = gridCol.DataColumn;
            Point currentCell = cell.DataGridView.CurrentCellAddress;
            bool hotMouse = (currentCell.Y == rowIndex && currentCell.X == cell.OwningColumn.Index);
            bool selectedCell = ((cellState & DataGridViewElementStates.Selected) > 0);
            bool activeRow = (cell.DataGridView.CurrentRow.Index == rowIndex);
            SimpleDataStyleOne style = dataRow.GetStyle(dataCol.ColumnIndex, hotMouse, selectedCell, activeRow);

            Rectangle backBounds = cellBounds;
            bool joinedCell = (joinMode == SimpleCellJoinMode.SingleCell || joinMode == SimpleCellJoinMode.FirstCell || joinMode == SimpleCellJoinMode.InnerCell || joinMode == SimpleCellJoinMode.LastCell);
            using (Brush brush = style.GetBrush(backBounds, joinedCell))
            {
                graphics.FillRectangle(brush, cellBounds);
            }

            paintParts = paintParts ^ DataGridViewPaintParts.Background; // ^ DataGridViewPaintParts.ContentBackground;

            cellStyle.ForeColor = style.ForeColor;
            cellStyle.SelectionForeColor = style.ForeColor;
        }
        /// <summary>
        /// V dané buňce vykreslí čáru, která znamená "Vztah"
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="cell"></param>
        /// <param name="clipBounds"></param>
        /// <param name="cellBounds"></param>
        /// <param name="rowIndex"></param>
        /// <param name="cellState"></param>
        /// <param name="value"></param>
        /// <param name="formattedValue"></param>
        /// <param name="errorText"></param>
        /// <param name="cellStyle"></param>
        /// <param name="advancedBorderStyle"></param>
        /// <param name="paintParts"></param>
        public static void PaintRelation(Graphics graphics, DataGridViewCell cell, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, ref DataGridViewPaintParts paintParts)
        {
            Rectangle line = new Rectangle(cellBounds.X + 1, cellBounds.Bottom - 4, cellBounds.Width - 4, 2);
            using (System.Drawing.Drawing2D.LinearGradientBrush lgb = new System.Drawing.Drawing2D.LinearGradientBrush(cellBounds, Color.BlueViolet, Color.Blue, 0f))
            {
                graphics.FillRectangle(lgb, line);
            }
        }
        /// <summary>
        /// Přečte z datové tabulky hodnotu pro danou buňku.
        /// Vrací hodnotu neformátovanou, tak jak je v tabulce.
        /// </summary>
        /// <param name="cell"></param>
        /// <returns></returns>
        public static object ReadValue(DataGridViewCell cell)
        {
            SimpleDataGridColumn column = cell.OwningColumn as SimpleDataGridColumn;
            SimpleDataGridRow row = cell.OwningRow as SimpleDataGridRow;
            SimpleDataRow dataRow = (row == null || column == null ? null : row.DataRow);
            if (dataRow == null) return null;
            RefreshToolTip(cell, dataRow, column);
            return dataRow[column.Index];
        }
        /// <summary>
        /// Do datové tabulky uloží hodnotu pro danou buňku.
        /// Hodnotu nijak neupravuje.
        /// </summary>
        /// <param name="cell"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool StoreValue(DataGridViewCell cell, object value)
        {
            SimpleDataGridColumn column = cell.OwningColumn as SimpleDataGridColumn;
            SimpleDataGridRow row = cell.OwningRow as SimpleDataGridRow;
            SimpleDataRow dataRow = (row == null || column == null ? null : row.DataRow);
            if (dataRow == null) return false;
            dataRow[column.Index] = value;
            return true;
        }
        /// <summary>
        /// Aktualizuje ToolTip pro danou buňku
        /// </summary>
        /// <param name="cell"></param>
        public static void RefreshToolTip(DataGridViewCell cell)
        {
            SimpleDataGridColumn column = cell.OwningColumn as SimpleDataGridColumn;
            SimpleDataGridRow row = cell.OwningRow as SimpleDataGridRow;
            SimpleDataRow dataRow = (row == null || column == null ? null : row.DataRow);
            if (dataRow == null) return;
            RefreshToolTip(cell, dataRow, column);
        }
        /// <summary>
        /// Aktualizuje ToolTip pro danou buňku
        /// </summary>
        /// <param name="cell"></param>
        /// <param name="dataRow"></param>
        /// <param name="column"></param>
        public static void RefreshToolTip(DataGridViewCell cell, SimpleDataRow dataRow, SimpleDataGridColumn column)
        {
            SimpleDataCell dataCell = dataRow.Cells[column.Index];
            if (dataCell.HasToolTipText)
                cell.ToolTipText = dataCell.ToolTipText;
            else
                cell.ToolTipText = null;
        }
        /// <summary>
        /// Určí režim slučování pro danou buňku ve směru Y
        /// </summary>
        /// <param name="cell"></param>
        /// <param name="formattedValue"></param>
        /// <param name="advancedBorderStyle"></param>
        /// <returns></returns>
        public static SimpleCellJoinMode DetectCellJoinY(DataGridViewCell cell, int rowIndex, ref object formattedValue, ref DataGridViewAdvancedBorderStyle advancedBorderStyle)
        {
            SimpleDataGrid grid = cell.DataGridView as SimpleDataGrid;
            SimpleCellJoinMode joinMode = grid.DataTable.DetectCellJoin(rowIndex, cell.ColumnIndex);
            switch (joinMode)
            {
                case SimpleCellJoinMode.FirstCell:
                    advancedBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.None;
                    break;
                case SimpleCellJoinMode.LastCell:
                    formattedValue = "";
                    advancedBorderStyle.Top = DataGridViewAdvancedCellBorderStyle.None;
                    break;
                case SimpleCellJoinMode.InnerCell:
                    formattedValue = "";
                    advancedBorderStyle.Top = DataGridViewAdvancedCellBorderStyle.None;
                    advancedBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.None;
                    break;
            }
            return joinMode;
        }
    }
    #endregion
    #region enum SimpleDataGridEditMode
    /// <summary>
    /// Možnosti editace v tabulce SimpleDataGrid
    /// </summary>
    public enum SimpleDataGridEditMode
    {
        /// <summary>
        /// Nelze editovat nic
        /// </summary>
        None,
        /// <summary>
        /// Lze editovat obsah, ale nelze přidávat / ubírat řádky
        /// </summary>
        EditContent,
        /// <summary>
        /// Lze editovat obsah, a je možno přidávat / ubírat řádky
        /// </summary>
        EditContentAndRows
    }
    #endregion
}
